#include "vigenerebreakerform.h"
#include "ui_vigenerebreakerform.h"

#include <QFile>
#include <QFileDialog>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlRecord>
#include <QMessageBox>
#include <QtDebug>

#include "cipher.h"
#include "task/findvigenerekeylenght.h"
#include "task/breakvigenere.h"
#include "loadingform.h"

VigenereBreakerForm::VigenereBreakerForm(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::VigenereBreakerForm)
{
    ui->setupUi(this);
}

VigenereBreakerForm::~VigenereBreakerForm()
{
    delete ui;
}

void VigenereBreakerForm::haveKeyLenght(int keyLenght)
{
    if (keyLenght == -1)
        QMessageBox::information(this, "Fail", "impossibile determinate la lunghezza della chiave.");
    else
        ui->keyLenght->setText(QString::number(keyLenght));
}

void VigenereBreakerForm::haveKeyLenght(QList<int> correlations)
{
    QString correlationString;
    for (int i = 1; i < correlations.length(); i++)
        correlationString.append(QString::number(i) + ": " + QString::number(correlations[i]) + ", ");
    QMessageBox::information(this, "Ambiguous choice", correlationString.prepend("please select manually the key length. correlation is:\n"));
}

void VigenereBreakerForm::broken(QByteArray key, QByteArray plaintext)
{
    ui->key->setBinaryData(key);
    ui->plainText->setBinaryData(plaintext);
}

void VigenereBreakerForm::on_friedman_clicked(bool checked)
{
    ui->maxLenght->setEnabled(!checked);
}

void VigenereBreakerForm::on_loadCipher_clicked()
{
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"));
    QFile input(fileName);
    input.open(QIODevice::ReadOnly);
    ui->cipherText->setBinaryData(input.readAll());
}

void VigenereBreakerForm::on_loadFreq_clicked()
{
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"));
    if (fileName.isNull()) return;
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName(fileName);

    if (!db.open())
    {
        qDebug() << "impossibile aprire il file";
        return;
    }

    QSqlQuery q = db.exec("SELECT * FROM frequencies WHERE klen <= 1");
    if (!q.isActive())
    {
        qDebug() << "il file delle frequenze non è valido (la tabella \"frequencies\" non esiste)";
        return;
    }
    Cipher c;
    if (!ui->binaryMode->isChecked()) c.setAsciiMode();
    alphabet = c.alphabet();

    langFrequencies.clear();
    for (int i = 0; i < alphabet.size(); i++) langFrequencies << 0.0;

    int keyField = q.record().indexOf("key");
    int freqField = q.record().indexOf("relFreq");
    if (keyField == -1 || freqField == -1)
    {
        qDebug() << "il file delle frequenze non è valido (campi non presenti nella tabella \"frequencies\")";
        return;
    }

    while (q.next())
    {
        int i = alphabet.indexOf(q.value(keyField).toByteArray());
        if (i == -1) return;
        langFrequencies[i] = q.value(freqField).toReal();
    }

    hasLoadedFreqs = true;
    ui->friedman->setEnabled(true);
    ui->break_2->setEnabled(true);
    ui->langFile->setText(fileName.split("/").last());
}

void VigenereBreakerForm::on_crackLenght_clicked()
{
    FindVigenereKeyLenght * fvkl = ui->friedman->isChecked() ?
                new FindVigenereKeyLenght(ui->cipherText->binaryData(),
                                          alphabet,
                                          langFrequencies,
                                          false,
                                          this) :
                new FindVigenereKeyLenght(ui->cipherText->binaryData(),
                                          alphabet,
                                          ui->maxLenght->text().toInt(),
                                          this);
    connect(fvkl, SIGNAL(done(int)), this, SLOT(haveKeyLenght(int)));
    connect(fvkl, SIGNAL(done(QList<int>)), this, SLOT(haveKeyLenght(QList<int>)));
    connect(fvkl, SIGNAL(finished()), fvkl, SLOT(deleteLater()));

    LoadingForm * busyLabel = new LoadingForm("Estimating Key Length...", this);
    busyLabel->move((rect().bottomRight() - busyLabel->rect().bottomRight())/2);
    busyLabel->show();
    connect(fvkl, SIGNAL(finished()), busyLabel, SLOT(deleteLater()));

    fvkl->start();
}

void VigenereBreakerForm::on_break_2_clicked()
{
    if (!hasLoadedFreqs) return;
    BreakVigenere * bv = new BreakVigenere(alphabet,
                                           langFrequencies,
                                           ui->keyLenght->text().toInt(),
                                           ui->cipherText->binaryData(),
                                           ui->xorMode->isChecked(),
                                           this);
    connect(bv, SIGNAL(done(QByteArray,QByteArray)), this, SLOT(broken(QByteArray,QByteArray)));
    connect(bv, SIGNAL(finished()), bv, SLOT(deleteLater()));

    LoadingForm * busyLabel = new LoadingForm("Breaking Vigenére...", this);
    busyLabel->move((rect().bottomRight() - busyLabel->rect().bottomRight())/2);
    busyLabel->show();
    connect(bv, SIGNAL(finished()), busyLabel, SLOT(deleteLater()));
    bv->start();
}

void VigenereBreakerForm::on_save_clicked()
{
    QString fileName = QFileDialog::getSaveFileName(this, tr("Open File"));
    QFile output(fileName);
    output.open(QIODevice::WriteOnly);
    output.write(ui->plainText->binaryData());
}

void VigenereBreakerForm::on_decrypt_clicked()
{
    Cipher vigenere(alphabet, ui->key->binaryData(), ui->xorMode->isChecked() ? Cipher::XORVigenere : Cipher::Vigenere, this);
    vigenere.setCipherText(ui->cipherText->binaryData());
    vigenere.decrypt();
    ui->plainText->setBinaryData(vigenere.plainText());
}

void VigenereBreakerForm::on_cEscape_clicked()
{
    ui->plainText->setEscapeMode(BinaryTextEdit::CEscape);
    ui->cipherText->setEscapeMode(BinaryTextEdit::CEscape);
    ui->key->setEscapeMode(BinaryTextEdit::CEscape);
}

void VigenereBreakerForm::on_URLEscape_clicked()
{
    ui->plainText->setEscapeMode(BinaryTextEdit::URLEscape);
    ui->cipherText->setEscapeMode(BinaryTextEdit::URLEscape);
    ui->key->setEscapeMode(BinaryTextEdit::URLEscape);
}

void VigenereBreakerForm::on_hex_clicked()
{
    ui->plainText->setEscapeMode(BinaryTextEdit::Hex);
    ui->cipherText->setEscapeMode(BinaryTextEdit::Hex);
    ui->key->setEscapeMode(BinaryTextEdit::Hex);
}

void VigenereBreakerForm::on_base64_clicked()
{
    ui->plainText->setEscapeMode(BinaryTextEdit::Base64);
    ui->cipherText->setEscapeMode(BinaryTextEdit::Base64);
    ui->key->setEscapeMode(BinaryTextEdit::Base64);
}

void VigenereBreakerForm::on_binaryMode_clicked(bool checked)
{
    if (checked)
    {
        if (ui->cEscape->isChecked())
            on_cEscape_clicked();
        else if (ui->URLEscape->isChecked())
            on_URLEscape_clicked();
        else if (ui->hex->isChecked())
            on_hex_clicked();
        else // ui->base64->isChecked()
            on_base64_clicked();
    }
    else
    {
        ui->plainText->setEscapeMode(BinaryTextEdit::None);
        ui->cipherText->setEscapeMode(BinaryTextEdit::None);
        ui->key->setEscapeMode(BinaryTextEdit::None);
    }
}
